<?php
// +----------------------------------------------------------------------
// | ThinkPHP [ WE CAN DO IT JUST THINK IT ]
// +----------------------------------------------------------------------
// | Copyright (c) 2006-2014 http://thinkphp.cn All rights reserved.
// +----------------------------------------------------------------------
// | Licensed ( http://www.apache.org/licenses/LICENSE-2.0 )
// +----------------------------------------------------------------------
// | Author: liu21st <liu21st@gmail.com>
// +----------------------------------------------------------------------
namespace Think;

/**
 * ThinkPHP 引导类
 */
class Think {
	
	// 类映射
	private static $_map = array ();
	
	// 实例化对象
	private static $_instance = array ();
	
	/**
	 * 应用程序初始化
	 * 
	 * @access public
	 * @return void
	 */
	static public function start() {
		// 注册AUTOLOAD方法
		spl_autoload_register ( 'Think\Think::autoload' );
		// 设定错误和异常处理
		register_shutdown_function ( 'Think\Think::fatalError' );
		set_error_handler ( 'Think\Think::appError' );
		set_exception_handler ( 'Think\Think::appException' );
		
		// 初始化文件存储方式
		Storage::connect ( STORAGE_TYPE );
		
		$runtimefile = RUNTIME_PATH . APP_MODE . '~runtime.php';
		if (! APP_DEBUG && Storage::has ( $runtimefile )) {
			Storage::load ( $runtimefile );
		} else {
			if (Storage::has ( $runtimefile ))
				Storage::unlink ( $runtimefile );
			$content = '';
			// 读取应用模式
			$mode = include is_file ( CONF_PATH . 'core.php' ) ? CONF_PATH . 'core.php' : MODE_PATH . APP_MODE . '.php';
			// 加载核心文件
			foreach ( $mode ['core'] as $file ) {
				if (is_file ( $file )) {
					include $file;
					if (! APP_DEBUG)
						$content .= compile ( $file );
				}
			}
			
			// 加载应用模式配置文件
			foreach ( $mode ['config'] as $key => $file ) {
				is_numeric ( $key ) ? C ( load_config ( $file ) ) : C ( $key, load_config ( $file ) );
			}
			
			// 读取当前应用模式对应的配置文件
			if ('common' != APP_MODE && is_file ( CONF_PATH . 'config_' . APP_MODE . CONF_EXT ))
				C ( load_config ( CONF_PATH . 'config_' . APP_MODE . CONF_EXT ) );
				
				// 加载模式别名定义
			if (isset ( $mode ['alias'] )) {
				self::addMap ( is_array ( $mode ['alias'] ) ? $mode ['alias'] : include $mode ['alias'] );
			}
			
			// 加载应用别名定义文件
			if (is_file ( CONF_PATH . 'alias.php' ))
				self::addMap ( include CONF_PATH . 'alias.php' );
				
				// 加载模式行为定义
			if (isset ( $mode ['tags'] )) {
				Hook::import ( is_array ( $mode ['tags'] ) ? $mode ['tags'] : include $mode ['tags'] );
			}
			
			// 加载应用行为定义
			if (is_file ( CONF_PATH . 'tags.php' ))
				
				// 允许应用增加开发模式配置定义
				Hook::import ( include CONF_PATH . 'tags.php' );
				
				// 加载框架底层语言包
			L ( include THINK_PATH . 'Lang/' . strtolower ( C ( 'DEFAULT_LANG' ) ) . '.php' );
			
			if (! APP_DEBUG) {
				$content .= "\nnamespace { Think\\Think::addMap(" . var_export ( self::$_map, true ) . ");";
				$content .= "\nL(" . var_export ( L (), true ) . ");\nC(" . var_export ( C (), true ) . ');Think\Hook::import(' . var_export ( Hook::get (), true ) . ');}';
				Storage::put ( $runtimefile, strip_whitespace ( '<?php ' . $content ) );
			} else {
				// 调试模式加载系统默认的配置文件
				C ( include THINK_PATH . 'Conf/debug.php' );
				// 读取应用调试配置文件
				if (is_file ( CONF_PATH . 'debug' . CONF_EXT ))
					C ( include CONF_PATH . 'debug' . CONF_EXT );
			}
		}
		
		// 读取当前应用状态对应的配置文件
		if (APP_STATUS && is_file ( CONF_PATH . APP_STATUS . CONF_EXT ))
			C ( include CONF_PATH . APP_STATUS . CONF_EXT );
			
			// 设置系统时区
		date_default_timezone_set ( C ( 'DEFAULT_TIMEZONE' ) );
		
		// 检查应用目录结构 如果不存在则自动创建
		if (C ( 'CHECK_APP_DIR' )) {
			$module = defined ( 'BIND_MODULE' ) ? BIND_MODULE : C ( 'DEFAULT_MODULE' );
			if (! is_dir ( APP_PATH . $module ) || ! is_dir ( LOG_PATH )) {
				// 检测应用目录结构
				Build::checkDir ( $module );
			}
		}
		
		// 记录加载文件时间
		G ( 'loadTime' );
		// 运行应用
		App::run ();
	}
	
	// 注册classmap
	static public function addMap($class, $map = '') {
		if (is_array ( $class )) {
			self::$_map = array_merge ( self::$_map, $class );
		} else {
			self::$_map [$class] = $map;
		}
	}
	
	// 获取classmap
	static public function getMap($class = '') {
		if ('' === $class) {
			return self::$_map;
		} elseif (isset ( self::$_map [$class] )) {
			return self::$_map [$class];
		} else {
			return null;
		}
	}
	
	/**
	 * 类库自动加载
	 * 
	 * @param string $class
	 *        	对象类名
	 * @return void
	 */
	public static function autoload($class) {
		// 检查是否存在映射
		if (isset ( self::$_map [$class] )) {
			include self::$_map [$class];
		} elseif (false !== strpos ( $class, '\\' )) {
			$name = strstr ( $class, '\\', true );
			if (in_array ( $name, array (
					'Think',
					'Org',
					'Behavior',
					'Com',
					'Vendor' 
			) ) || is_dir ( LIB_PATH . $name )) {
				// Library目录下面的命名空间自动定位
				$path = LIB_PATH;
			} else {
				// 检测自定义命名空间 否则就以模块为命名空间
				$namespace = C ( 'AUTOLOAD_NAMESPACE' );
				$path = isset ( $namespace [$name] ) ? dirname ( $namespace [$name] ) . '/' : APP_PATH;
			}
			$filename = $path . str_replace ( '\\', '/', $class ) . EXT;
			if (is_file ( $filename )) {
				// Win环境下面严格区分大小写
				if (IS_WIN && false === strpos ( str_replace ( '/', '\\', realpath ( $filename ) ), $class . EXT )) {
					return;
				}
				include $filename;
			}
		} elseif (! C ( 'APP_USE_NAMESPACE' )) {
			// 自动加载的类库层
			foreach ( explode ( ',', C ( 'APP_AUTOLOAD_LAYER' ) ) as $layer ) {
				if (substr ( $class, - strlen ( $layer ) ) == $layer) {
					if (require_cache ( MODULE_PATH . $layer . '/' . $class . EXT )) {
						return;
					}
				}
			}
			// 根据自动加载路径设置进行尝试搜索
			foreach ( explode ( ',', C ( 'APP_AUTOLOAD_PATH' ) ) as $path ) {
				if (import ( $path . '.' . $class ))
					
					// 如果加载类成功则返回
					return;
			}
		}
	}
	
	/**
	 * 取得对象实例 支持调用类的静态方法
	 * 
	 * @param string $class
	 *        	对象类名
	 * @param string $method
	 *        	类的静态方法名
	 * @return object
	 */
	static public function instance($class, $method = '') {
		$identify = $class . $method;
		if (! isset ( self::$_instance [$identify] )) {
			if (class_exists ( $class )) {
				$o = new $class ();
				if (! empty ( $method ) && method_exists ( $o, $method ))
					self::$_instance [$identify] = call_user_func ( array (
							&$o,
							$method 
					) );
				else
					self::$_instance [$identify] = $o;
			} else
				self::halt ( L ( '_CLASS_NOT_EXIST_' ) . ':' . $class );
		}
		return self::$_instance [$identify];
	}
	
	/**
	 * 自定义异常处理
	 * 
	 * @access public
	 * @param mixed $e
	 *        	异常对象
	 */
	static public function appException($e) {
		$error = array ();
		$error ['message'] = $e->getMessage ();
		$trace = $e->getTrace ();
		if ('E' == $trace [0] ['function']) {
			$error ['file'] = $trace [0] ['file'];
			$error ['line'] = $trace [0] ['line'];
		} else {
			$error ['file'] = $e->getFile ();
			$error ['line'] = $e->getLine ();
		}
		$error ['trace'] = $e->getTraceAsString ();
		Log::record ( $error ['message'], Log::ERR );
		// 发送404信息
		header ( 'HTTP/1.1 404 Not Found' );
		header ( 'Status:404 Not Found' );
		self::halt ( $error );
	}
	
	/**
	 * 自定义错误处理
	 * 
	 * @access public
	 * @param int $errno
	 *        	错误类型
	 * @param string $errstr
	 *        	错误信息
	 * @param string $errfile
	 *        	错误文件
	 * @param int $errline
	 *        	错误行数
	 * @return void
	 */
	static public function appError($errno, $errstr, $errfile, $errline) {
		switch ($errno) {
			case E_ERROR :
			case E_PARSE :
			case E_CORE_ERROR :
			case E_COMPILE_ERROR :
			case E_USER_ERROR :
				ob_end_clean ();
				$errorStr = "$errstr " . $errfile . " 第 $errline 行.";
				if (C ( 'LOG_RECORD' ))
					Log::write ( "[$errno] " . $errorStr, Log::ERR );
				self::halt ( $errorStr );
				break;
			default :
				$errorStr = "[$errno] $errstr " . $errfile . " 第 $errline 行.";
				self::trace ( $errorStr, '', 'NOTIC' );
				break;
		}
	}
	
	// 致命错误捕获
	static public function fatalError() {
		Log::save ();
		if ($e = error_get_last ()) {
			switch ($e ['type']) {
				case E_ERROR :
				case E_PARSE :
				case E_CORE_ERROR :
				case E_COMPILE_ERROR :
				case E_USER_ERROR :
					ob_end_clean ();
					self::halt ( $e );
					break;
			}
		}
	}
	
	/**
	 * 错误输出
	 * 
	 * @param mixed $error
	 *        	错误
	 * @return void
	 */
	static public function halt($error) {
		$e = array ();
		if (APP_DEBUG || IS_CLI) {
			// 调试模式下输出错误信息
			if (! is_array ( $error )) {
				$trace = debug_backtrace ();
				$e ['message'] = $error;
				$e ['file'] = $trace [0] ['file'];
				$e ['line'] = $trace [0] ['line'];
				ob_start ();
				debug_print_backtrace ();
				$e ['trace'] = ob_get_clean ();
			} else {
				$e = $error;
			}
			if (IS_CLI) {
				exit ( iconv ( 'UTF-8', 'gbk', $e ['message'] ) . PHP_EOL . 'FILE: ' . $e ['file'] . '(' . $e ['line'] . ')' . PHP_EOL . $e ['trace'] );
			}
		} else {
			// 否则定向到错误页面
			$error_page = C ( 'ERROR_PAGE' );
			if (! empty ( $error_page )) {
				redirect ( $error_page );
			} else {
				$message = is_array ( $error ) ? $error ['message'] : $error;
				$e ['message'] = C ( 'SHOW_ERROR_MSG' ) ? $message : C ( 'ERROR_MESSAGE' );
			}
		}
		// 包含异常页面模板
		$exceptionFile = C ( 'TMPL_EXCEPTION_FILE', null, THINK_PATH . 'Tpl/think_exception.tpl' );
		include $exceptionFile;
		exit ();
	}
	
	/**
	 * 添加和获取页面Trace记录
	 * 
	 * @param string $value
	 *        	变量
	 * @param string $label
	 *        	标签
	 * @param string $level
	 *        	日志级别(或者页面Trace的选项卡)
	 * @param boolean $record
	 *        	是否记录日志
	 * @return void|array
	 */
	static public function trace($value = '[think]', $label = '', $level = 'DEBUG', $record = false) {
		static $_trace = array ();
		if ('[think]' === $value) { // 获取trace信息
			return $_trace;
		} else {
			$info = ($label ? $label . ':' : '') . print_r ( $value, true );
			$level = strtoupper ( $level );
			
			if ((defined ( 'IS_AJAX' ) && IS_AJAX) || ! C ( 'SHOW_PAGE_TRACE' ) || $record) {
				Log::record ( $info, $level, $record );
			} else {
				if (! isset ( $_trace [$level] ) || count ( $_trace [$level] ) > C ( 'TRACE_MAX_RECORD' )) {
					$_trace [$level] = array ();
				}
				$_trace [$level] [] = $info;
			}
		}
	}
}
